package com.sinaapp.gavinzhang.GSpider;

import com.sinaapp.gavinzhang.GSpider.Exception.*;
import com.sinaapp.gavinzhang.GSpider.config.*;
import com.sinaapp.gavinzhang.GSpider.core.*;
import com.sinaapp.gavinzhang.GSpider.http.HttpServer;
import com.sinaapp.gavinzhang.GSpider.process.*;
import com.sinaapp.gavinzhang.GSpider.process.Process;
import org.apache.log4j.Logger;

import java.util.*;
import java.util.concurrent.*;
import java.util.regex.*;


public class GSpider implements iGSpider {

	private static Logger log = Logger.getLogger(GSpider.class);

	/** System*/
    /** 控制用线程池*/
    private ExecutorService controlExecutor;
    /** 任务线程池*/
    private ExecutorService taskExecutor;
    /** start开始标志*/
    private boolean isStart;
	/** stop结束标志*/
	private boolean isStop;


	private PageProcessorConfig pageProcessorConfig;
	private GSpiderConfig gSpiderConfig;

	public GSpider()
    {
		this(GSpiderConfig.getDefaultConfig(), PageProcessorConfig.getDefaultPageProcessorConfig());
    }

	public GSpider(GSpiderConfig gSpiderConfig,PageProcessorConfig pageProcessorConfig) {
		this.gSpiderConfig = gSpiderConfig;
		this.pageProcessorConfig = pageProcessorConfig;
	}

	public GSpider(AbstractGWebUrlList pendingUrlList, AbstractGWebUrlList visitedUrlList, int controlExecutorNum, int taskExecutorNum, AbstractProcess process, List<Pattern> patterns, AbstractPageProcessor pageProcessor) {
		this(pendingUrlList, visitedUrlList, controlExecutorNum, taskExecutorNum, process, patterns, pageProcessor, new DefaultHttpProcessorInterceptor());
	}

	public GSpider(AbstractGWebUrlList pendingUrlList, AbstractGWebUrlList visitedUrlList, int controlExecutorNum, int taskExecutorNum, AbstractProcess process, List<Pattern> patterns, AbstractPageProcessor pageProcessor,HttpProcessorInterceptor httpProcessorInterceptor) {
		this.gSpiderConfig = new GSpiderConfig();
		gSpiderConfig.setTaskExecutorNum(taskExecutorNum);
		gSpiderConfig.setControlExecutorNum(controlExecutorNum);
		gSpiderConfig.setProcess(process);

		this.pageProcessorConfig = new PageProcessorConfig();
		pageProcessorConfig.setPendingUrlList(pendingUrlList);
		pageProcessorConfig.setVisitedUrlList(visitedUrlList);
		pageProcessorConfig.setPatterns(patterns);
		pageProcessorConfig.setPageProcessor(pageProcessor);
		pageProcessorConfig.setHttpProcessorInterceptor(httpProcessorInterceptor);
	}

	//系统初始化
	public GSpider init() {

		log.info(Constants.GSPIDER_INIT_SUCCESS);

    	isStart = false;
		isStop = false;
        setExecutor();
		//http服务器
		controlExecutor.execute(new HttpServer(taskExecutor, gSpiderConfig, pageProcessorConfig));
		log.info(Constants.HTTP_SERVER_INIT_SUCCESS);

		System.out.println(Constants.HTTP_SERVER_INIT_SUCCESS);
		/** 加载默认处理*/
		if(pageProcessorConfig.getPageProcessor() == null)
		{
			pageProcessorConfig.setPageProcessor(new DefaultPageProcessor());
			log.warn(Constants.LOAD_DEFAULT_PAGE_PROCESSOR);
		}
		if(pageProcessorConfig.getVisitedUrlList() == null)
		{
			pageProcessorConfig.setVisitedUrlList(new VisitedWebUrlList());
		}
        return this;
    }
	
	/**
	 * check()方法的作用是检查任务信息是否完备
	 */
	public GSpider check()throws GSpiderInitException
	{
		if(pageProcessorConfig.getPatterns() == null || pageProcessorConfig.getPatterns().size()==0) {
			log.error(Constants.NO_REGEX);
			throw new GSpiderInitException(Constants.NO_REGEX);
		}
		if(pageProcessorConfig.getPendingUrlList().size() == 0) {
			log.error(Constants.NO_INIT_URL);
			throw new GSpiderInitException(Constants.NO_INIT_URL);
		}
		return this;
	}
    
	public void controlInit()throws GSpiderInitException
    {
		init();
    	check();
        log.info(Constants.GSPIDER_CONTROL_INIT_SUCCESS);
		Scanner sc = new Scanner(System.in);
		String command;
		while (true)
		{
			System.out.println(Constants.COMMAND_LINE);
			command = sc.nextLine();
			switch (command) {
				case Constants.START:
					start();
					break;
				case Constants.STOP:
					sc.close();
					stop();
					System.exit(0);
				case Constants.ADD:
					System.out.println("url input:");
					addWebUrl(sc.nextLine());
					break;
				case Constants.LOOK:
					look();
					break;
				default:break;
			}
		}
    }
    
    public void start() {
        if (!isStart) {
        	log.info(Constants.GSPIDER_START_SUCCESS);
			isStop = false;
            isStart = true;
        }
        else {
            log.warn(Constants.GSPIDER_START_ERROR);
            return;
        }
		Process process;
		for (int i = 0; i < gSpiderConfig.getTaskExecutorNum(); i++) {
			Class<?> c = gSpiderConfig.getProcess().getClass();
			try {
				process = (Process) c.newInstance();
			} catch (InstantiationException | IllegalAccessException e) {
				throw new RuntimeException("process error");
			}
			process.setPageProcessorConfig(pageProcessorConfig);
			taskExecutor.execute(process);
		}
    }
    
    public void stop() {
        taskExecutor.shutdownNow();
        controlExecutor.shutdownNow();
        while (!taskExecutor.isShutdown() && !controlExecutor.isShutdown() && ((ThreadPoolExecutor)taskExecutor).getActiveCount()!=0)isStop = false;
		//help gc
		taskExecutor = null;
		controlExecutor = null;

		isStop = true;
		isStart = false;
        log.info(Constants.GSPIDER_STOP);
    }

	@Override
	public void addWebUrl(String webUrl) {
		//默认加载CurrentWebUrlList
		if(pageProcessorConfig.getPendingUrlList() == null) {
			log.error(Constants.PENDING_NULL);
			throw new RuntimeException(Constants.PENDING_NULL);
		}
    	if(!webUrl.matches("^http://")) {
    		log.warn(Constants.ADD_URL_ERROR + webUrl);
    		return;
    	}
    	pageProcessorConfig.getPendingUrlList().add(webUrl);
	}

	public void addWebUrl(List<String> webUrlList) {
		//默认加载CurrentWebUrlList
		if(pageProcessorConfig.getPendingUrlList() == null) {
			log.error(Constants.PENDING_NULL);
			throw new RuntimeException(Constants.PENDING_NULL);
		}
		Pattern p = Pattern.compile("^http://");
		for (String webUrl : webUrlList) {
			Matcher m = p.matcher(webUrl);
			if(!m.find()) {
				log.warn(Constants.ADD_URL_ERROR + webUrl);
				return;
			}
			pageProcessorConfig.getPendingUrlList().add(webUrl);
		}
	}

	public GSpider addRegex(Pattern regex) {
		if(pageProcessorConfig.getPatterns() == null) {
			log.error(Constants.PATTERNS_NULL);
			throw new RuntimeException(Constants.PATTERNS_NULL);
		}
		pageProcessorConfig.getPatterns().add(regex);
		return this;
	}


	public GSpider addRegex(List<Pattern> patterns) {
		if(pageProcessorConfig.getPatterns() == null) {
			log.error(Constants.PATTERNS_NULL);
			throw new RuntimeException(Constants.PATTERNS_NULL);
		}
		pageProcessorConfig.getPatterns().addAll(patterns);
		return this;
	}

    public void look()
    {
        int ActiveCount = ((ThreadPoolExecutor)taskExecutor).getActiveCount();
        int CorePoolSize = ((ThreadPoolExecutor)taskExecutor).getCorePoolSize();
        int PoolSize = ((ThreadPoolExecutor)taskExecutor).getPoolSize();
        long CompletedTaskCount = ((ThreadPoolExecutor)taskExecutor).getCompletedTaskCount();
        long TaskCount = ((ThreadPoolExecutor)taskExecutor).getTaskCount();
        System.out.println("*********************************");
        System.out.println("ActiveCount:            "+ActiveCount);
        System.out.println("CorePoolSize:           "+CorePoolSize);
        System.out.println("PollSize:               "+PoolSize);
        System.out.println("CompletedTaskCount:     "+CompletedTaskCount);
        System.out.println("TaskCount:              "+TaskCount);
        System.out.println("foudUrlList:            " + pageProcessorConfig.getFoundUrlList().size());
        System.out.println("currentUrlList:         " + pageProcessorConfig.getCurrentUrlList().size());
        System.out.println("*********************************");
        log.info(Constants.LOOK);
    }

    /** 创建线程池*/
	private void setExecutor() {
		this.controlExecutor = Executors.newFixedThreadPool(gSpiderConfig.getControlExecutorNum());
		this.taskExecutor = Executors.newFixedThreadPool(gSpiderConfig.getTaskExecutorNum());
	}

	public long getPendingUrlCount() {
		return pageProcessorConfig.getPendingUrlList().size();
	}

	public GSpider setPendingUrlList(AbstractGWebUrlList pendingUrlList) {
		pageProcessorConfig.setPendingUrlList(pendingUrlList);
		return this;
	}

	public GSpider setVisitedWebUrlList(AbstractGWebUrlList visitedUrlList) {
		pageProcessorConfig.setVisitedUrlList(visitedUrlList);
		return this;
	}

	@Override
	public AbstractGWebUrlList getVisitedUrlList() {
			return pageProcessorConfig.getVisitedUrlList();
	}

	public GSpider setControlExecutorNum(int controlExecutorNum) {
		gSpiderConfig.setControlExecutorNum(controlExecutorNum);
		return this;
	}

	public GSpider setTaskExecutorNum(int taskExecutorNum) {
		gSpiderConfig.setTaskExecutorNum(taskExecutorNum);
	    return this;
	}
	    
	public GSpider setProcess(AbstractProcess process) {
	 	gSpiderConfig.setProcess(process);
	   	return this;
	}

	public GSpider setPatterns(List<Pattern> patterns) {
		pageProcessorConfig.setPatterns(patterns);
		return this;
	}

	public GSpider setPageProcessor(PageProcessor pp)throws GSpiderIllegimateException
	{
		if (isStart)
		{
			throw new GSpiderIllegimateException("爬虫已经启动，添加处理类失败");
		}
		pageProcessorConfig.setPageProcessor(pp);
		return this;
	}

	//获取线程池存活线程数
	public int getActiveCount(){
		return ((ThreadPoolExecutor)taskExecutor).getActiveCount();
	}



	@Deprecated
	public long getCurrentUrlCount() {
		return pageProcessorConfig.getPendingUrlList().size();
	}
	@Deprecated
	public GSpider setRegex(Pattern regex) {
		if(pageProcessorConfig.getPatternUrls() == null) {
			pageProcessorConfig.setPatternUrls(new ArrayList<Pattern>());
		}
		pageProcessorConfig.getPatternUrls().add(regex);
		return this;
	}
	@Deprecated
	public GSpider setFindDestination(AbstractPageProcessor fd) {
		pageProcessorConfig.setPageProcessor(fd);
		return this;
	}
	@Deprecated
	public GSpider setCurrentWebUrlList(AbstractGWebUrlList currentUrlList) {
		pageProcessorConfig.setPendingUrlList(currentUrlList);
		return this;
	}
	@Deprecated
	public GSpider addListP(List<Pattern> listP) {
		if(pageProcessorConfig.getListP() == null) {
			pageProcessorConfig.setListP(new ArrayList<Pattern>());
		}
		pageProcessorConfig.getListP().addAll(listP);
		return this;
	}
	@Deprecated
	public GSpider setFoundWebUrlList(AbstractGWebUrlList foundWebUrlList) {
		pageProcessorConfig.setVisitedUrlList(foundWebUrlList);
		return this;
	}
	@Deprecated
	public GSpider setListP(List<Pattern> patternUrls) {
		pageProcessorConfig.setPatternUrls(patternUrls);
		return this;
	}
	@Deprecated
	public GSpider setPatternUrls(List<Pattern> patternUrls) {
		pageProcessorConfig.setPatternUrls(patternUrls);
		return this;
	}
}
